package com.thxr.sso.server.util;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

/**
 * @Author: silence.Z
 * @CreateTime: 2020/11/9 15:20
 * @Description:
 */
public class ZimbraUtil {

    static String newline = System.getProperty("line.separator");

    /** Creates a new instance of Main */
    public ZimbraUtil() {
    }

    public static void main(String args[]) {
        String account = getAccount();   // Account name you are trying to login
        String by = "name";         // Specifies how account should be determined
        String timestamp = "";      // Current Unix date and time
        String expires = "0";       // Length of time in milliseconds to expire the auth
        String preAuthKey = "                                                                "; // Server-generated preauth key
        String preAuthValue = "";   // Calculated value for preauth
        String preAuthUrl = "https://mail.domain.com/service/preauth?";  // URL to use preauth
        String openBrowser = "rundll32 url.dll,FileProtocolHandler ";   // Command to open browser after OS is determined

        // Generate timestamp
        Date date = new Date();
        timestamp = "" + date.getTime();

        // Assign values needed to calculate preauth
        HashMap<String,String> params = new HashMap<String,String>();
        params.put("account", account);
        params.put("by", by); // needs to be part of hmac
        params.put("timestamp", timestamp);
        params.put("expires", expires);

        // Compute preAuthValue
        preAuthValue = computePreAuth(params, preAuthKey);

        // Construct preAuthUrl
        preAuthUrl += constructUrl(account, expires, timestamp, preAuthValue);

        // Open Browser;
        try {
            Runtime.getRuntime().exec(openBrowser + preAuthUrl);
        }
        catch (IOException e) {
            System.out.println("Unable to open browser.");
            System.exit(1);
        }
    }

    /**
     * Constructs the URL needed to authorize
     *
     * Example url:
     * https://mail.domain.com/service/preauth?account=john.doe@domain.com&expires=0&timestamp=1135280708088&preauth=b248f6cfd027edd45c5369f8490125204772f844
     */
    public static String constructUrl(String useAccount, String useExpires, String useTimestamp, String usePreAuthValue) {
        String url = "";
        // Account part
        url += "account=" + useAccount + "&";
        // Expires part
        url += "expires=" + useExpires + "&";
        // Timestamp part
        url += "timestamp=" + useTimestamp + "&";
        // PreAuth part
        url += "preauth=" + usePreAuthValue;
        return url;
    }

    /**
     * Provides the account name to login as
     *
     * Retrieves the Windows login name and uses that as the Zimbra login name
     */
    public static String getAccount() {
        String username = System.getProperty("user.name");
        return username;
    }

    /**
     * Computes the preauth string
     */
    public static String computePreAuth(Map<String,String> params, String key) {
        TreeSet<String> names = new TreeSet<String>(params.keySet());
        StringBuilder sb = new StringBuilder();
        for (String name : names) {
            if (sb.length() > 0) {
                sb.append('|');
            }
            sb.append(params.get(name));
        }
        return getHmac(sb.toString(), key.getBytes());
    }

    /**
     * Computes the preAuth excryption key
     */
    private static String getHmac(String data, byte[] key) {
        try {
            ByteKey bk = new ByteKey(key);
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(bk);
            return toHex(mac.doFinal(data.getBytes()));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("fatal error", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("fatal error", e);
        }
    }

    static class ByteKey implements SecretKey {
        private byte[] mKey;

        ByteKey(byte[] key) {
            mKey = (byte[]) key.clone();
        }

        @Override
        public byte[] getEncoded() {
            return mKey;
        }

        @Override
        public String getAlgorithm() {
            return "HmacSHA1";
        }

        @Override
        public String getFormat() {
            return "RAW";
        }
    }

    public static String toHex(byte[] data) {
        StringBuilder sb = new StringBuilder(data.length * 2);
        for (int i=0; i<data.length; i++ ) {
            sb.append(hex[(data[i] & 0xf0) >>> 4]);
            sb.append(hex[data[i] & 0x0f] );
        }
        return sb.toString();
    }

    private static final char[] hex = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};

}